using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using IQIGame.Onigao.GamePlay;
using System;
using System.Collections.Generic;

namespace IQIGame.Onigao.Logic
{
    public class BattleTriggerController : TController
    {
        private BetterLinkedList<BattleTrigger>[] trigger = new BetterLinkedList<BattleTrigger>[(int)EBattleTriggerType.Max];
        private Dictionary<int, BetterLinkedListNode<BattleTrigger>> nodeDict = new Dictionary<int, BetterLinkedListNode<BattleTrigger>>();
        private int nodeID = 0;

        #region AddListener

        private int Add(BetterLinkedList<BattleTrigger> rList, int priority, BattleTrigger rTrigger)
        {
            var nID = ++this.nodeID;
            this.nodeDict.Add(nID, rList.AddBefore(rList.GetAfterNodeWithPriority(priority), rTrigger));
            return nID;
        }
        // no parameters
        public int AddListener(EBattleTriggerType eventType, int priority, Action handler)
        {
            var nIndex = (int)eventType;
            if (this.trigger[nIndex] == null)
            {
                this.trigger[nIndex] = new BetterLinkedList<BattleTrigger>();
            }

            var rTrigger = ClassPool.Get<BattleTrigger>();
            rTrigger.SetParam(eventType, handler, priority);
            var rList = this.trigger[nIndex];
            return this.Add(rList, priority, rTrigger);
        }
        // one parameters
        public int AddListener<T>(EBattleTriggerType eventType, int priority, Action<T> handler)
        {
            var nIndex = (int)eventType;
            if (this.trigger[nIndex] == null)
            {
                this.trigger[nIndex] = new BetterLinkedList<BattleTrigger>();
            }

            var rTrigger = ClassPool.Get<BattleTrigger>();
            rTrigger.SetParam(eventType, handler, priority);
            var rList = this.trigger[nIndex];
            return this.Add(rList, priority, rTrigger);
        }
        // two parameters
        public int AddListener<T1, T2>(EBattleTriggerType eventType, int priority, Action<T1, T2> handler)
        {
            var nIndex = (int)eventType;
            if (this.trigger[nIndex] == null)
            {
                this.trigger[nIndex] = new BetterLinkedList<BattleTrigger>();
            }

            var rTrigger = ClassPool.Get<BattleTrigger>();
            rTrigger.SetParam(eventType, handler, priority);
            var rList = this.trigger[nIndex];
            return this.Add(rList, priority, rTrigger);
        }
        // three parameters
        public int AddListener<T1, T2, T3>(EBattleTriggerType eventType, int priority, Action<T1, T2, T3> handler)
        {
            var nIndex = (int)eventType;
            if (this.trigger[nIndex] == null)
            {
                this.trigger[nIndex] = new BetterLinkedList<BattleTrigger>();
            }

            var rTrigger = ClassPool.Get<BattleTrigger>();
            rTrigger.SetParam(eventType, handler, priority);
            var rList = this.trigger[nIndex];
            return this.Add(rList, priority, rTrigger);
        }
        // four parameters
        public int AddListener<T1, T2, T3, T4>(EBattleTriggerType eventType, int priority, Action<T1, T2, T3, T4> handler)
        {
            var nIndex = (int)eventType;
            if (this.trigger[nIndex] == null)
            {
                this.trigger[nIndex] = new BetterLinkedList<BattleTrigger>();
            }

            var rTrigger = ClassPool.Get<BattleTrigger>();
            rTrigger.SetParam(eventType, handler, priority);
            var rList = this.trigger[nIndex];
            return this.Add(rList, priority, rTrigger);
        }
        // five parameters
        public int AddListener<T1, T2, T3, T4, T5>(EBattleTriggerType eventType, int priority, Action<T1, T2, T3, T4, T5> handler)
        {
            var nIndex = (int)eventType;
            if (this.trigger[nIndex] == null)
            {
                this.trigger[nIndex] = new BetterLinkedList<BattleTrigger>();
            }

            var rTrigger = ClassPool.Get<BattleTrigger>();
            rTrigger.SetParam(eventType, handler, priority);
            var rList = this.trigger[nIndex];
            return this.Add(rList, priority, rTrigger);
        }
        #endregion

        #region RemoveListener

        public void RemoveListener(int triggerID)
        {
            if (this.nodeDict.TryGetValue(triggerID, out var rNode))
            {
                var nIndex = (int)rNode.value.eventType;
                this.nodeDict.Remove(triggerID);

                if (this.trigger[nIndex] != null)
                {
                    this.trigger[nIndex].RemoveNode(rNode);
                }
            }
        }
        #endregion

        #region Broadcast
        // no paramters
        public void Broadcast(EBattleTriggerType eventType)
        {
            var nIndex = (int)eventType;
            var rTriggerList = this.trigger[nIndex];
            if (rTriggerList == null)
            {
                return;
            }

            var rList = ListPool<BattleTrigger>.Get();
            var rNode = rTriggerList.FirstNode;
            for (; rNode != null; rNode = rNode.next)
            {
                rList.Add(rNode.value);
            }

            for (int i = 0; i < rList.Count; i++)
            {
                var rTrigger = rList[i];
                if (rTrigger.enabled && rTrigger.mEvent is Action callback)
                {
                    callback();
                }
                else
                {
                    throw new Exception(string.Format("Broadcasting message \"{0}\" but listeners have a different signature than the broadcaster.", eventType));
                }
            }
            ListPool<BattleTrigger>.Put(rList);
        }
        // one paramters
        public void Broadcast<T>(EBattleTriggerType eventType, T arg1)
        {
            var nIndex = (int)eventType;
            var rTriggerList = this.trigger[nIndex];
            if (rTriggerList == null)
            {
                return;
            }

            var rList = ListPool<BattleTrigger>.Get();
            var rNode = rTriggerList.FirstNode;
            for (; rNode != null; rNode = rNode.next)
            {
                rList.Add(rNode.value);
            }

            for (int i = 0; i < rList.Count; i++)
            {
                var rTrigger = rList[i];
                if (rTrigger.enabled && rTrigger.mEvent is Action<T> callback)
                {
                    callback(arg1);
                }
                else
                {
                    throw new Exception(string.Format("Broadcasting message \"{0}\" but listeners have a different signature than the broadcaster.", eventType));
                }
            }
            ListPool<BattleTrigger>.Put(rList);
        }
        // two paramters
        public void Broadcast<T1, T2>(EBattleTriggerType eventType, T1 arg1, T2 arg2)
        {
            var nIndex = (int)eventType;
            var rTriggerList = this.trigger[nIndex];
            if (rTriggerList == null)
            {
                return;
            }

            var rList = ListPool<BattleTrigger>.Get();
            var rNode = rTriggerList.FirstNode;
            for (; rNode != null; rNode = rNode.next)
            {
                rList.Add(rNode.value);
            }

            for (int i = 0; i < rList.Count; i++)
            {
                var rTrigger = rList[i];
                if (rTrigger.enabled && rTrigger.mEvent is Action<T1, T2> callback)
                {
                    callback(arg1, arg2);
                }
                else
                {
                    throw new Exception(string.Format("Broadcasting message \"{0}\" but listeners have a different signature than the broadcaster.", eventType));
                }
            }
            ListPool<BattleTrigger>.Put(rList);
        }
        // three paramters
        public void Broadcast<T1, T2, T3>(EBattleTriggerType eventType, T1 arg1, T2 arg2, T3 arg3)
        {
            var nIndex = (int)eventType;
            var rTriggerList = this.trigger[nIndex];
            if (rTriggerList == null)
            {
                return;
            }

            var rList = ListPool<BattleTrigger>.Get();
            var rNode = rTriggerList.FirstNode;
            for (; rNode != null; rNode = rNode.next)
            {
                rList.Add(rNode.value);
            }

            for (int i = 0; i < rList.Count; i++)
            {
                var rTrigger = rList[i];
                if (rTrigger.enabled && rTrigger.mEvent is Action<T1, T2, T3> callback)
                {
                    callback(arg1, arg2, arg3);
                }
                else
                {
                    throw new Exception(string.Format("Broadcasting message \"{0}\" but listeners have a different signature than the broadcaster.", eventType));
                }
            }
            ListPool<BattleTrigger>.Put(rList);
        }
        // four paramters
        public void Broadcast<T1, T2, T3, T4>(EBattleTriggerType eventType, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
        {
            var nIndex = (int)eventType;
            var rTriggerList = this.trigger[nIndex];
            if (rTriggerList == null)
            {
                return;
            }

            var rList = ListPool<BattleTrigger>.Get();
            var rNode = rTriggerList.FirstNode;
            for (; rNode != null; rNode = rNode.next)
            {
                rList.Add(rNode.value);
            }

            for (int i = 0; i < rList.Count; i++)
            {
                var rTrigger = rList[i];
                if (rTrigger.enabled && rTrigger.mEvent is Action<T1, T2, T3, T4> callback)
                {
                    callback(arg1, arg2, arg3, arg4);
                }
                else
                {
                    throw new Exception(string.Format("Broadcasting message \"{0}\" but listeners have a different signature than the broadcaster.", eventType));
                }
            }
            ListPool<BattleTrigger>.Put(rList);
        }
        // five paramters
        public void Broadcast<T1, T2, T3, T4, T5>(EBattleTriggerType eventType, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
        {
            var nIndex = (int)eventType;
            var rTriggerList = this.trigger[nIndex];
            if (rTriggerList == null)
            {
                return;
            }

            var rList = ListPool<BattleTrigger>.Get();
            var rNode = rTriggerList.FirstNode;
            for (; rNode != null; rNode = rNode.next)
            {
                rList.Add(rNode.value);
            }

            for (int i = 0; i < rList.Count; i++)
            {
                var rTrigger = rList[i];
                if (rTrigger.enabled && rTrigger.mEvent is Action<T1, T2, T3, T4, T5> callback)
                {
                    callback(arg1, arg2, arg3, arg4, arg5);
                }
                else
                {
                    throw new Exception(string.Format("Broadcasting message \"{0}\" but listeners have a different signature than the broadcaster.", eventType));
                }
            }
            ListPool<BattleTrigger>.Put(rList);
        }
        #endregion

        protected override void OnDispose()
        {
            for (int i = 0; i < this.trigger.Length; i++)
            {
                this.trigger[i]?.Clear();
            }
            this.nodeDict.Clear();
            this.nodeID = 0;
        }
    }

    public class BattleTrigger : IReusableClass
    {
        public Delegate mEvent { get; private set; }
        public int priority { get; private set; }
        public EBattleTriggerType eventType { get; private set; }

        public bool enabled = false;

        public void SetParam(EBattleTriggerType rEventType, Delegate rEvent, int nProprity)
        {
            this.eventType = rEventType;
            this.mEvent = rEvent;
            this.priority = nProprity;
            this.enabled = true;
        }

        public uint MaxStore => 5;
        public void OnReset()
        {
            this.mEvent = null;
            this.priority = 0;
            this.enabled = false;
        }
    }

    public static class ExtendBattleTrigger
    {
        public static BetterLinkedListNode<BattleTrigger> GetAfterNodeWithPriority(this BetterLinkedList<BattleTrigger> rList, int nPriority)
        {
            var rNode = rList.FirstNode;
            for (; rNode != null; rNode = rNode.next)
            {
                if (rNode.value.priority > nPriority)
                {
                    return rNode;
                }
            }
            return null;
        }

        public static BetterLinkedListNode<BattleTrigger> GetFirstTriggerWithPriority(this BetterLinkedList<BattleTrigger> rList, int nPriority)
        {
            var rNode = rList.LastNode;
            for (; rNode != null; rNode = rNode.prev)
            {
                if (rNode.value.priority < nPriority)
                {
                    return rNode.next;
                }
            }
            return null;
        }
    }

    /// 战斗触发器Key，不可以指定值
    public enum EBattleTriggerType
    {
        Min,
        Battle_BattleStart,
        Battle_RoundStart,
        Battle_TurnStart,
        Battle_SkillExecuteState,
        Battle_TurnEnd,
        Battle_RoundEnd,
        Battle_BattleEnd,

        Battle_ExecuteSkill,
        Battle_SkillDamage,
        Battle_SkillDamageTempModify,
        Battle_SkillEffectTempModify,

        Battle_AddBuff,
        Battle_RemoveBuff,
        Battle_DestroyBuff,
        Battle_UnitDie,

        Battle_SetCustomProperty,
        Max,
    }
}